﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _15Puzzle_sharp
{
    class FifteenPuzzle
    {
        private int[,] board = new int[,] {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 0}};
        private int freeX = 0;
        private int freeY = 0;
        private int moves = 0;

        public FifteenPuzzle()
        {
        }
        private void DrawBoard()
        {
            Console.Clear();
            for (int i = 0; i < board.GetLength(0); i++)
            {
                Console.WriteLine("---------------------");
                for (int j = 0; j < board.GetLength(1); j++)
                {
                    Console.Write("| " + board[i, j].ToString("##").PadLeft(2) + " ");
                }
                Console.WriteLine("|");
            }
            Console.WriteLine("---------------------");
        }
        private bool GameComplete()
        {
            int k = 1;
            for (int i = 0; i < board.GetLength(0); i++)
            {
                for (int j = 0; j < board.GetLength(1); j++)
                {
                    if (board[i, j] != k)
                    {
                        return false;
                    }
                    k = ++k % 16;
                }
            }
            return true;
        }
        private void MoveTiles(int xStep, int yStep)
        {
            board[freeX, freeY] = board[freeX + xStep, freeY + yStep];
            freeX += xStep;
            freeY += yStep;
            board[freeX, freeY] = 0;
            moves++;
            DrawBoard();
        }
        private void GenerateBoard()
        {
            Random rng = new Random();
            int[] nums = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};

            //Randomize the board until a solvable board is generated
            do
            {
                for (int i = 0; i < nums.Length; i += 1)
                {
                    int swapIndex = rng.Next(nums.Length);
                    if (swapIndex != i)
                    {
                        int tempint = nums[i];
                        nums[i] = nums[swapIndex];
                        nums[swapIndex] = tempint;
                    }
                }
            } while (!CheckSolvable(ref nums));

            //Input the generated board to the game board
            int k = 0;
            for (int i = 0; i < board.GetLength(0); i++)
            {
                for (int j = 0; j < board.GetLength(1); j++)
                {
                    board[i, j] = nums[k];
                    k++;
                }
            }

            //Find the empty space 
            for (int i = 0; i < board.GetLength(0); i++)
            {
                for (int j = 0; j < board.GetLength(1); j++)
                {
                    if (board[i, j] == 0)
                    {
                        freeX = i;
                        freeY = j;
                    }
                }
            }
        }
        private bool CheckSolvable(ref int[] iref)
        {
            int inversions = 0, emptyRow = 0;
	        for (int i = 0; i < iref.Length; i++)
	        {
		        if (iref[i] == 0)
		        {
			        if ((i > 3 && i < 8) || i > 11)
			        {
				        emptyRow = 1;
			        }
		        }
		        else
		        {
			        for (int j = i + 1; j < iref.Length; j++)
			        {
				        if (iref[i] > iref[j] && iref[j] != 0){
					        inversions++;
				        }
			        }
		        }
	        }
	        if (((inversions + emptyRow) % 2) == 1)
	        {
		        return true;
	        }
	        return false;
        }
        public void Run()
        {
            while (true)
            {
                GenerateBoard();
                DrawBoard();
                while (!GameComplete())//Player control loop
                {
                    ConsoleKeyInfo input = Console.ReadKey(true);
                    if (input.Key == ConsoleKey.LeftArrow && freeY < 3)
                    {
                        MoveTiles(0, 1);
                    }
                    else if (input.Key == ConsoleKey.RightArrow && freeY > 0)
                    {
                        MoveTiles(0, -1);
                    }
                    else if (input.Key == ConsoleKey.DownArrow && freeX > 0)
                    {
                        MoveTiles(-1, 0);
                    }
                    else if (input.Key == ConsoleKey.UpArrow && freeX < 3)
                    {
                        MoveTiles(1, 0);
                    }
                    else if (input.Key == ConsoleKey.Escape)
                    {
                        break;
                    }
                }//end player control loop
                DrawBoard();
                if (GameComplete())
                {
                    Console.WriteLine("Game completed after " + moves + " moves");
                }
                else
                {
                    Console.WriteLine("Game exited after " + moves + " moves..");
                }
                Console.WriteLine("Press ESCAPE to exit or any key to play again..");
                if (Console.ReadKey(true).Key == ConsoleKey.Escape)
                {
                    break;
                }
            }
        }
    }
}
